<?php

namespace App\Service\Article;

use App\Enums\BaseErrorCode;
use App\Exceptions\Custom\ResponseHttpException;
use App\Models\Article\ArticleModel;
use App\Models\Article\ArticleStatisticsDayModel;
use App\Models\Article\ArticleStatisticsLogsModel;
use App\Models\Article\ArticleUserRelationModel;
use App\Service\BaseService;
use App\Service\OpenShop\User\RmsUserService;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class ArticleOperateService extends BaseService
{

    const ARTICLE_REDIS_KEY         = 'articleData:%s:%d';// 文章数据 articleData:20230615:1
    const ARTICLE_REDIS_EXPIRE_TIME = 3 * 86400;          // 文章数据 缓存过期时间 3天

    // 商家-首页数据埋点记录类型
    const ARTICLE_DATA_CLICK_REDIS_KEY         = 'merchantClick:%s';// 埋点记录数据 merchantClick:{date} {type}
    const ARTICLE_DATA_CLICK_REDIS_EXPIRE_TIME = 3 * 86400;         // 埋点记录数据 缓存过期时间 3天

    const ARTICLE_DATA_TYPE_1 = 1;
    const ARTICLE_DATA_TYPE_2 = 2;
    const ARTICLE_DATA_TYPE_3 = 3;

    const ARTICLE_DATA_TYPE_TEXT_MAPPING = [
        self::ARTICLE_DATA_TYPE_1 => 'banner',
        self::ARTICLE_DATA_TYPE_2 => '专题',
        self::ARTICLE_DATA_TYPE_3 => '内容',
    ];

    /**
     * 商家-交互操作(点赞、分享、收藏)
     * @param int $type
     * @param int $articleId
     * @param int $userId
     */
    public static function operate(int $type, int $articleId, int $userId = 0)
    {
        /*if ($type == ArticleStatisticsLogsModel::TYPE_SHOW) {// 曝光展示 需要判断多长时间算一次 todo

        }*/
        $column = ArticleStatisticsLogsModel::getColumn($type);
        if (!$column) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "类型不匹配");
        }
        $count = 1;
        if (in_array($type, array_keys(ArticleStatisticsLogsModel::TYPE_CANCEL_TEXT))) {//取消操作
            $count = -1;
        }
        $articleModel = ArticleModel::query()->find($articleId);
        if (!$articleModel) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "未找到文章");
        }
        if ($userId) {
            // 文章关联用户操作表
            $articleUserRelType = ArticleUserRelationModel::getType($type);
            $articleUserRel     = ArticleUserRelationModel::getInfo($articleId, $userId, $articleUserRelType);
            if (!empty($articleUserRel) && $articleUserRel->id) {
                $status = ArticleUserRelationModel::STATUS_SUCCESS;
                if (in_array($type, array_keys(ArticleStatisticsLogsModel::TYPE_CANCEL_TEXT))) {//取消操作
                    $status = ArticleUserRelationModel::STATUS_CANCEL;
                }

                // 一个人对文章只能点赞/收藏 一次
                if (
                    $status == $articleUserRel->status &&
                    in_array($articleUserRelType, [ArticleUserRelationModel::TYPE_LIKE, ArticleUserRelationModel::TYPE_COLLECT])
                ) {
                    Log::info(
                        'operateUserRelRes:', [
                            'type'               => $type,
                            'articleId'          => $articleId,
                            'userId'             => $userId,
                            'articleUserRelType' => $articleUserRelType,
                            'status'             => $articleUserRel->status,
                        ]
                    );
                    return;
                }
            }
        }

        DB::beginTransaction();
        try {

            // 文章关联用户操作表
            if ($userId) {
                if (!empty($articleUserRel) && $articleUserRel->id) {
                    if ($status != $articleUserRel->status) {
                        ArticleUserRelationModel::query()->where('id', $articleUserRel->id)
                            ->update(['status' => $status, 'updatedByUid' => $userId]);
                    }
                } else {
                    $relData = [
                        'articleId'    => $articleId,
                        'type'         => $articleUserRelType,
                        'createdByUid' => $userId,
                        'createdAt'    => time(),
                        'status'       => ArticleUserRelationModel::STATUS_SUCCESS,
                    ];
                    ArticleUserRelationModel::query()->insert($relData);
                }
            }

            // 文章表字段修改
            $incrementRes = ArticleModel::query()
                ->where('id', $articleId)
                ->when(
                    $count < 0, function ($query) use ($count, $column) {
                    $query->where($column, '>=', abs($count));
                }
                )
                ->increment($column, $count);
            if ($incrementRes) {

                // 文章_操作日志
                $logData = [
                    'articleId'    => $articleId,
                    'type'         => $type,
                    'createdByUid' => $userId,
                    'createdAt'    => time(),
                ];
                ArticleStatisticsLogsModel::query()->insert($logData);
            }
            DB::commit();
            if ($incrementRes) {
                // redis hash
                self::articleRedis($articleId, $column, $count);
            }
            Log::info(
                'operateArticle：', [
                'articleId'    => $articleId,
                'type'         => $type,
                'createdByUid' => $userId,
                'incrementRes' => $incrementRes,
            ]
            );
        } catch (Exception $exception) {
            DB::rollBack();
            Log::error(
                'operateArticleErr：', [
                    'articleId'    => $articleId,
                    'type'         => $type,
                    'createdByUid' => $userId,
                    'msg'          => $exception->getMessage(),
                ]
            );
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "文章操作失败");
        }
    }

    /**
     * 单个文章缓存数据
     * @param int $articleId
     * @param string $column
     * @param int $count
     */
    public static function articleRedis(int $articleId, string $column, int $count)
    {
        $day = date('Ymd');

        // 单个文章
        $articleKey = sprintf(self::ARTICLE_REDIS_KEY, $day, $articleId);
        $exists     = Redis::exists($articleKey);
        Redis::hIncrBy($articleKey, $column, $count);
        if (!$exists) {
            Redis::expire($articleKey, self::ARTICLE_REDIS_EXPIRE_TIME);
        }

        // 全部文章
        $articleTotalKey = sprintf(self::ARTICLE_REDIS_KEY, $day, 0);
        $exists          = Redis::exists($articleTotalKey);
        Redis::hIncrBy($articleTotalKey, $column, $count);
        if (!$exists) {
            Redis::expire($articleTotalKey, self::ARTICLE_REDIS_EXPIRE_TIME);
        }
    }


    /**
     * 数据统计列表
     * @return array
     */
    public static function statisticsTotal(): array
    {

        // 今天
        $day = date('Y-m-d');
        // 昨天
        $yesDay = date('Y-m-d', strtotime('-1 day'));
        $list   = self::getArticleList(0, $yesDay, $day)['totalList'];
        return [
            'totalData'    => $list[$day] ?? [],
            'yesTotalData' => $list[$yesDay] ?? [],
        ];
    }

    /**
     * 文章列表
     * @param int $type
     * @param int $hotType
     * @param int $pageSize
     * @param int $pageNum
     * @return array
     */
    public static function articleList(int $type, int $hotType, int $pageSize, int $pageNum): array
    {
        $paginator = ArticleModel::query()
            ->when(
                $type, function ($query) use ($type) {
                $query->where("type", $type);//内容分类 1 - 文章  2 - 视频
            }
            )
            ->when(
                $hotType, function ($query) use ($hotType) {
                if ($hotType == 1) {
                    $query->orderByDesc('supportNum');//热门分类排序字段  1 - 点赞  2 - 收藏
                } else {
                    $query->orderByDesc('collectNum');
                }
            }
            )
            ->orderByDesc('id')
            ->paginate($pageSize, ['*'], 'pageNum', $pageNum);

        $userIds   = collect($paginator->items())->pluck('createdByUid')->toArray();
        $userInfos = collect(RmsUserService::getRmsUserInfoByUserIds($userIds)['data'])->keyBy('id');

        $lists = $paginator->map(
            function (ArticleModel $articleModel) use ($userInfos) {
                return [
                    'id'            => $articleModel->id,
                    'type'          => $articleModel->type,
                    'typeText'      => ArticleModel::TYPE_TEXT[$articleModel->type] ?? "",
                    'title'         => $articleModel->title,
                    'createdAt'     => $articleModel->createdAt->toDateTimeString(),                                             //创建时间
                    'effectiveTime' => $articleModel->effectiveTime ? date('Y-m-d H:i:s', $articleModel->effectiveTime) : "",    //生效时间
                    'supportNum'    => $articleModel->supportNum,                                                                //点赞数
                    'collectNum'    => $articleModel->collectNum,                                                                //收藏数
                    'forwardNum'    => $articleModel->forwardNum,                                                                //转发数
                    'readNum'       => $articleModel->readNum,                                                                   //阅读数
                    'showNum'       => $articleModel->showNum,                                                                   //展示数
                    'createdByUid'  => $articleModel->createdByUid,
                    'userName'      => $userInfos[$articleModel->createdByUid]['username'] ?? '',
                ];
            }
        );

        return [
            'pageNum'   => $paginator->currentPage(),
            'pageSize'  => $paginator->perPage(),
            'pageCount' => ceil($paginator->total() / $paginator->perPage()),
            'count'     => $paginator->total(),
            'lists'     => $lists,
        ];

    }


    /**
     * 文章数据统计列表
     * @param int $id
     * @param string $statisticalStartAt
     * @param string $statisticalEndAt
     * @return array[]
     */
    public static function getArticleList(int $id, string $statisticalStartAt, string $statisticalEndAt): array
    {
        $startTime = strtotime($statisticalStartAt);
        $endTime   = strtotime($statisticalEndAt);

        $totalList = [];
        $diffDay   = ($endTime - $startTime) / 86400;
        for ($i = 0; $i <= $diffDay; $i++) {
            $totalList[date('Y-m-d', $startTime + $i * 86400)] = [
                'supportNum'   => 0,
                'collectNum'   => 0,
                'forwardNum'   => 0,
                'readNum'      => 0,
                'showNum'      => 0,
                'totalArticle' => 0,
            ];
        }
        ArticleStatisticsDayModel::query()
            ->whereBetween('dateAt', [$startTime, $endTime])
            ->where('articleId', $id)
            ->get()
            ->map(
                function ($item) use (&$totalList) {
                    $key = date('Y-m-d', $item->dateAt);
                    switch ($item->source) {
                        case ArticleStatisticsDayModel::TYPE_LIKE:
                            $totalList[$key]['supportNum'] = $item->newNum;
                            break;
                        case ArticleStatisticsDayModel::TYPE_COLLECT:
                            $totalList[$key]['collectNum'] = $item->newNum;
                            break;
                        case ArticleStatisticsDayModel::TYPE_SHARE:
                            $totalList[$key]['forwardNum'] = $item->newNum;
                            break;
                        case ArticleStatisticsDayModel::TYPE_READ:
                            $totalList[$key]['readNum'] = $item->newNum;
                            break;
                        case ArticleStatisticsDayModel::TYPE_SHOW:
                            $totalList[$key]['showNum'] = $item->newNum;
                            break;
                        case ArticleStatisticsDayModel::TYPE_ARTICLE:
                            $totalList[$key]['totalArticle'] = $item->newNum;
                            break;
                    }
                }
            );

        if($endTime >= Carbon::now()->startOfDay()->timestamp){
            //获取当天数据
            $totalList[date('Y-m-d')] = self::getTotalArticleRedisData(date('Ymd'), $id);
        }

        return ['totalList' => $totalList];
    }

    /**
     * 获取全部文章缓存数据
     * @param string $day
     * @param int $id
     * @return mixed
     */
    public static function getTotalArticleRedisData(string $day, int $id = 0)
    {
        $key       = sprintf(self::ARTICLE_REDIS_KEY, $day, $id);
        $totalData = Redis::hGetAll($key);

        $totalData['supportNum']   = $totalData['supportNum'] ?? 0;
        $totalData['collectNum']   = $totalData['collectNum'] ?? 0;
        $totalData['forwardNum']   = $totalData['forwardNum'] ?? 0;
        $totalData['readNum']      = $totalData['readNum'] ?? 0;
        $totalData['showNum']      = $totalData['showNum'] ?? 0;
        $totalData['totalArticle'] = $totalData['totalArticle'] ?? 0;
        if (!$id && date('Ymd') == $day) {
            $totalData['totalArticle'] = ArticleModel::getCount();
        }
        return $totalData;
    }

    /**
     * 首页数据埋点记录
     * @param int $type
     * @return mixed
     */
    public static function click(int $type): array
    {
        $day    = date('Ymd');
        $key    = sprintf(self::ARTICLE_DATA_CLICK_REDIS_KEY, $day);
        $exists = Redis::exists($key);
        Redis::hIncrBy($key, $type, 1);
        if (!$exists) {
            Redis::expire($key, self::ARTICLE_DATA_CLICK_REDIS_EXPIRE_TIME);
        }

        return [];
    }

    /**
     * 获取首页数据埋点记录数据
     * @param string $date
     * @return mixed
     */
    public static function getClickRedisData(string $date): array
    {
        if (!$date) {
            $date = date('Ymd', strtotime('-1 day'));// 默认查昨天
        }
        $key = sprintf(self::ARTICLE_DATA_CLICK_REDIS_KEY, $date);
        $rdData = Redis::hGetAll($key);

        // 按模板返回数据
        $template = ArticleOperateService::ARTICLE_DATA_TYPE_TEXT_MAPPING;
        foreach ($template as $type => &$v) {
            $v = (int)($rdData[$type] ?? 0);
        }

        return $template;
    }

    /**
     * 夸父商家热门内容top10(统计逻辑:按24h的阅读数高低进行排序) 今天查询昨天的数据
     * @param string $date
     * @param int $limit
     * @return mixed
     */
    public static function getTopArticleByDate(string $date, $limit = 10): array
    {
        if (!$date) {
            $date = date('Y-m-d', strtotime('-1 day'));// 默认查昨天
        }
        $topArticleData = ArticleStatisticsDayModel::query()
            ->where('dateAt', strtotime($date))
            ->where('articleId', '>', 0)// 0为所有文章统计
            ->where('source', ArticleStatisticsDayModel::TYPE_READ)// 阅读
            ->select(['articleId','dateAt'])
            ->orderByDesc('netIncNum')
            ->limit($limit)
            ->get()
            ->keyBy('articleId')
            ->toArray();

        $topArticleIds = array_column($topArticleData, 'articleId');
        if ($topArticleIds) {
            ArticleStatisticsDayModel::query()
                ->with(['articleRel'])
                ->where('dateAt', strtotime($date))
                ->whereIn('articleId', $topArticleIds)
                ->select(['articleId', 'netIncNum', 'source'])
                ->get()
                ->map(
                    function ($item) use (&$topArticleData) {
                        if (isset($topArticleData[$item->articleId])) {
                            $topArticleData[$item->articleId]['title'] = $item->articleRel->title ?? '';
                            switch ($item->source) {
                                case ArticleStatisticsDayModel::TYPE_SHOW:// 展示数
                                    $topArticleData[$item->articleId]['showCount'] = $item->netIncNum;
                                    break;
                                case ArticleStatisticsDayModel::TYPE_READ:// 阅读数
                                    $topArticleData[$item->articleId]['readCount'] = $item->netIncNum;
                                    break;
                                case ArticleStatisticsDayModel::TYPE_LIKE:// 点赞
                                    $topArticleData[$item->articleId]['likeCount'] = $item->netIncNum;
                                    break;
                                case ArticleStatisticsDayModel::TYPE_SHARE:// 分享
                                    $topArticleData[$item->articleId]['shareCount'] = $item->netIncNum;
                                    break;
                                case ArticleStatisticsDayModel::TYPE_COLLECT:// 收藏
                                    $topArticleData[$item->articleId]['collectCount'] = $item->netIncNum;
                                    break;
                            }
                        }
                    }
                );
            return array_values($topArticleData);
        }
        return [];
    }
}
